#!/usr/bin/perl -w
###############################################################################
# Copyright (c) 2011 @ Spreadtrum Communication Inc.
# Spreadtrum Confidential Properity. All rights are reserved.
#
# File name   : regs_builder.pl
# Author      : gary.gao@spreadtrum.com
#               junhao.zheng@spreadtrum.com
# Date        : 2011/05/09
# Description : register builder
#
# Version     : 0.01 - regs_builder.log added.
#
###############################################################################

use strict;
use warnings;

#unshift(@INC,"./scripts");
#require ("reg_parser.pl") ;
#require ("reg_manual_gen.pl") ;

###############################################################################

#{{{

###############################################################################
# Copyright (c) 2011 @ Spreadtrum Communication Inc.
# Spreadtrum Confidential Properity. All rights are reserved.
#
# File name   : reg_parser.pl
# Author      : gary.gao@spreadtrum.com
# Date        : 2011/05/04
# Description : parser register description excel file to generate follow files.
#                - XML file for ovm rgm used. (xxx.spirit)
#                - Register define for vlog testbench used. (xxx_reg_def.sv)
#                - Register w/r task for vlog testbench used. (xxx_reg_task.sv)
#                - Register define for c file header. (xxx_reg_def.h)   
#                - Register w/r function for c code. (xxx_reg_fun.c)
#                - Register table for doc generator. (xxx_reg_tbl.txt) 
# Version     : 1.0
#
###############################################################################

###############################################################################
#
# A Spreadsheet::ParseExcel utility to parse modle register description excel,
# and genenate register description in XML format for ovm_rgm .
#

use warnings;
use strict;
use Spreadsheet::ParseExcel;
#use Spreadsheet::ParseExcel::FmtUnicode;
use Encode;
#use Encode qw/encode decode/;



#use encoding 'utf8', STDIN=>'gb2312', STDOUT=>'gb2312', VLOG_TASK_FH=>'gb2312';
#use encoding 'utf8', STDIN=>'euc_cn', STDOUT=>'euc_cn';

# TODO. Should add configuration options. Maybe cell merging.
# Option to display upto a certain column/row. Should use Pod::Usage also.


#{{{ parameter define

#my $user = `whoami`;
my $user = $ENV{USERNAME} ; 
my $date = `echo %date%`;
my $time = `echo %time%`;

chomp $user;
chomp $date;
chomp $time;

$user =~ s/^(\w*\\+)*//g;
$date =~ /(\d+-\d+-\d+)/g;
$date = $1;

$time =~ /(\d+:\d+:\d+)/g;
$time = $1;

#printf "Data: ".$date." : $time\n";
#printf "User: ".$user."\n";
#exit;

my $input_fn;
my $gen_dir;

my $prj="SC9610";
my $mem_map="LTE";

my $vlog_def_gen=1;
my $c_def_gen=1;

my $disp_row;
my $disp_col;

my $module_name_row = 0;
my $module_name_col = 1;
my $module_base_addr_row = 1;
my $module_base_addr_col = 1;
my $module_range_row = 2;
my $module_range_col = 1;

my $reg_offset_col = 0;
my $reg_name_col = 1;
my $reg_short_dsc_col = 2;
my $reg_size_col = 3;
my $reg_field_col = 4;
my $reg_field_name_col = 5;
my $reg_field_access_col = 6;
my $reg_reset_col = 7;
my $reg_dsc_col = 8;

my $reg_offset_cell;
my $reg_name_cell;
my $reg_short_dsc_cell;
my $reg_size_cell;
my $reg_field_cell;
my $reg_field_name_cell;
my $reg_field_access_cell;
my $reg_reset_cell;
my $reg_dsc_cell;

my $reg_offset_val;
my $reg_name_val;
my $reg_short_dsc_val;
my $reg_size_val;
my $reg_field_val;
my $reg_field_name_val;
my $reg_field_access_val;
my $reg_reset_val;
my $reg_dsc_val;

my $reset_val=0;
my $mask_val=0;
my $field_start_addr;
my $field_end_addr;
my $field_width;
my $last_field_start_addr = 0;
my $last_field_end_addr = 0;

my $module_name;
my $module_base_addr = 0;
my $module_mem_range;

my %module_block;
my %register;
#my @reg_ar;
my %reg_ar_by_name;
my %reg_ar_by_offset;

my $reg_is_parsing=0;
#my $first_fld_parsing=0;

my %rgm_block;

my $extr_reg_num=0;

my $max_name_len=0;

my @field_array = ();
my @field_dsc_array = ();

my $rb_log_fn;

#}}}

#$input_fn = $ARGV[0] or die "Parsing error: must specify filename.";
#if(-e $input_fn && )

#reg_parser_main();

sub reg_parser_main{

   #$input_fn = shift @ARGV;
   #$gen_dir = shift @ARGV;
    ($input_fn, $gen_dir) = @_ ;
    chomp $input_fn;
    chomp $gen_dir;
    
    printf "file: $input_fn, dir: $gen_dir\n";
    #exit;


    reg_xls_parser();
    
    print_register_array();
    
    rgm_block_build();
    
    #block_hir_print(%rgm_block);
    
    rgm_gen();
    
    vlog_reg_def_gen();
    
    vlog_reg_task_gen();
}

###############################################################################
#
# reg_xls_parser().
#
# Run the main section of the program to extract the register information
# from the Excel.
#
###############################################################################
sub reg_xls_parser{#{{{

    #my $filename = $ARGV[0] or die "Parsing error: must specify filename.";
    my $parser   = Spreadsheet::ParseExcel->new();
    my $workbook = $parser->parse($input_fn);
   #my $oFmtC = Spreadsheet::ParseExcel::FmtUnicode->new(Unicode_Map=>"CP936");
   #my $workbook = $parser->parse($input_fn, $oFmtC);

    #my $module_name;
    #my $base_addr;
    #my $mem_range;

    #my $reg_addr;
    #my $reg_name;
    #my $reg_acs;

    print RB_LOG_FH "Register xls parsing...\n";
    print "Register xls parsing...\n";

    binmode STDOUT, ':encoding(utf8)';

    # Check for errors from parse().
    if ( !defined $workbook ) {
        print RB_LOG_FH "Parsing error: ", $parser->error(), ".\n";
        die "Parsing error: ", $parser->error(), ".\n";
    }

    # Iterate through the worksheets.
    for my $worksheet ( $workbook->worksheets() ) {

        # Get the cell range for the worksheet.
        my ( $row_min, $row_max ) = $worksheet->row_range();
        my ( $col_min, $col_max ) = $worksheet->col_range();

        #printf "row_range($row_min, $row_max)\n";
        #printf "col_range($col_min, $col_max)\n";
        #exit;
        # Max values of -1 indicate that the worksheet doesn't contain data,
        # in which case we set the max to (0, 0) so that one cell is shown.
        $row_max = 0 if $row_max == -1;
        $col_max = 0 if $col_max == -1;

        my $column_count   = $col_max + 1;
        my $worksheet_name = $worksheet->get_name();

        # Iterate through the cell data and print out each row.
        for my $row ( 0 .. $row_max) {

            # The first item of the row data is the row number.
            #my @row_data = ( $row + 1 );
            my $cell;

            if ($row == $module_name_row){
                $cell = $worksheet->get_cell(0, 0);
                if (!$cell){
                    next;
                }
                #print $cell->value."\n";
                #exit;
                if ($cell->value =~ /Module_Name/){
                    $cell = $worksheet->get_cell ($row, 1);
                    if (!$cell){
                        next;
                    }
                    $module_name = $cell->value ();
                    $module_name = join "_", split " ", $module_name;
                    if ($module_name eq ''){
                        print RB_LOG_FH "Parsing error: module name is empty!\n";
                        die "Parsing error: module name is empty!";
                    }
                    #print $module_name."\n";
                    #print $cell->value."\n";
                }
                else{
                    print RB_LOG_FH "Parsing error: can't found the module name at the first row!\n";
                    die "Parsing error: can't found the module name at the first row!";
                }
            }

            if ($row == $module_base_addr_row){
                $cell = $worksheet->get_cell ($row, 0) ;
                if (!$cell){
                    next;
                }
                if ($cell->value =~ /BASE_ADDR/){
                    $cell = $worksheet->get_cell($row, 1) ;
                    if (!$cell){
                        next;
                    }
                    $module_base_addr = $cell->value();
                    chomp ($module_base_addr);
                    if ($module_base_addr !~ /0x[\da-fA-F]+/g){
                        print RB_LOG_FH "Parsing error: BASE_ADDR($module_base_addr) format error!\n";
                        die "Parsing error: BASE_ADDR($module_base_addr) format error!";
                    }
                    #print "BASE_ADDR=$base_addr\n";
                }
                else{
                    cov2disp ($module_base_addr, 0);
                    print RB_LOG_FH "Parsing error: can't found the BASE_ADDR at the $disp_row row!\n";
                    die "Parsing error: can't found the BASE_ADDR at the $disp_row row!";
                }
            }

            if ($row == $module_range_row){
                $cell = $worksheet->get_cell ($row, 0) ;
                if (!$cell){
                    next;
                }
                if ($cell->value =~ /RANGE/){
                    $cell = $worksheet->get_cell ($row, 1) ;
                    if (!$cell){
                        next;
                    }
                    $module_mem_range = $cell->value;
                    chomp ($module_mem_range);
                    if ($module_mem_range !~ /0x[\da-fA-F]+/g){
                        print RB_LOG_FH "Parsing error: RANGE($module_mem_range) format error!\n";
                        die "Parsing error: RANGE($module_mem_range) format error!";
                    }
                    #print "RANGE=$module_mem_range\n";
                }
                else{
                    cov2disp ($module_base_addr_row, 0);
                    print RB_LOG_FH "Parsing error: can't found the RANGE at the $disp_row row!\n";
                    die "Parsing error: can't found the RANGE at the $disp_row row!";
                }
            }

            if ($row > 2){
                # To find register Offset
                $reg_offset_cell = $worksheet->get_cell ($row, $reg_offset_col);
                if (!$reg_offset_cell){
                    next;
                }
                $reg_offset_val = $reg_offset_cell->value();

                #print "Found a reg offset $reg_offset_val\n";

                chomp ($reg_offset_val);

                $reg_offset_val =~ s/\s//g;
                if ($reg_offset_val =~ /^0x[\da-fA-F]+$/){
                    # To find register name
                    $reg_name_cell = $worksheet->get_cell ($row, $reg_name_col);
                    if (!$reg_name_cell){
                        next;
                    }
                    $reg_name_val = $reg_name_cell->value;
                    $reg_name_val =~ s/\s//g;
                    #print "Found a reg name $reg_name_val\n";

                    # To find register short description
                    $reg_short_dsc_cell = $worksheet->get_cell($row, $reg_short_dsc_col);
                    if(!$reg_short_dsc_cell){
                        cov2disp($row, $reg_short_dsc_cell);
                        print RB_LOG_FH "Parsing error: register short description can't be empty @($disp_row, $disp_col)!\n";
                        die "Parsing error: register short description can't be empty @($disp_row, $disp_col)!";
                    }
                    $reg_short_dsc_val = $reg_short_dsc_cell->value;
                    if($reg_short_dsc_cell =~ /^\s*$/){
                        cov2disp($row, $reg_short_dsc_cell);
                        print RB_LOG_FH "Parsing error: register short description can't be empty @($disp_row, $disp_col)!\n";
                        die "Parsing error: register short description can't be empty @($disp_row, $disp_col)!";
                    }

                    # To find register size
                    $reg_size_cell = $worksheet->get_cell($row, $reg_size_col);
                    if(!$reg_size_cell){
                        next;
                    }
                    $reg_size_val = $reg_size_cell->value;
                    $reg_size_val =~ s/\s//g;
                    if($reg_size_val !~ /^\d+$/){
                        cov2disp($row, $reg_size_col);
                        print RB_LOG_FH "Parsing error: reg size invalid @ ($disp_row, $disp_col).\n";
                        die "Parsing error: reg size invalid @ ($disp_row, $disp_col).";
                    }

                    # To find register descriptrion
                    $reg_dsc_cell = $worksheet->get_cell($row, $reg_dsc_col);
                    if(!$reg_dsc_cell){
                        $reg_dsc_val = $reg_name_val;
                        #next;
                    }
                    else {
                        $reg_dsc_val = $reg_dsc_cell->value;
                    }

                    if($reg_dsc_val =~ /^\s*$/){
                        $reg_dsc_val = $reg_name_val;
                    }

                    $reset_val = 0;
                    $mask_val = 0;
                    if ($last_field_end_addr != 0){
                        cov2disp ($row, 0);
                        print RB_LOG_FH "Parsing error: last field addr invalid @ ($disp_row, $disp_col).\n";
                        die "Parsing error: last field addr invalid @ ($disp_row, $disp_col).";
                    }
                    $last_field_start_addr = -1;
                    $last_field_end_addr = -1;

                    #print "A new register($reg_name_val) found\n";

                    %register = ();
                    #if(exists $register{name})
                    $register{name} = $reg_name_val;
                    $register{addressOffset} = $reg_offset_val;
                    $register{size} = $reg_size_val;
                    $register{reg_dsc} = $reg_dsc_val;
                    $register{reg_short_dsc} = $reg_short_dsc_val;
                    ##############################
                    ########################
                    ### CODING TO HERE....
                    ####################
                    ###########
                    #$register{access} = $reg_field_access_val;

                    $reg_is_parsing = 1;
                    @field_array = ();
                    @field_dsc_array = ();
                    #printf "reg name len is %d\n", cal_str_len($reg_name_val);
                    #$first_fld_parsing = 1;
                    #print $register{name}."\n";
                    #&print_register (%register);
                    #print @reg_ar."\n";
                    #exit;
                }
                elsif ($reg_offset_val eq ''){
                    if ($reg_is_parsing == 1){
                        #print "Processing regsiter($reg_name_val)\n";

                        # Parsing field name
                        $reg_field_name_cell = $worksheet->get_cell ($row, $reg_field_name_col);
                        if (!$reg_field_name_cell){
                            next;
                        }
                        $reg_field_name_val = $reg_field_name_cell->value;
                        #print "Found a reg field: $reg_field_name_val\n";
                        #$reg_field_name_val =~ s/\s//g;
                        $reg_field_name_val =~ s/^\s+//g;
                        $reg_field_name_val =~ s/\s+$//g;
                        $reg_field_name_val =~ s/\s+/_/g;
                        if($reg_field_name_val eq ''){
                            cov2disp($row, $reg_field_name_col);
                            print RB_LOG_FH "Parsing field name erroe: field name missing @ ($disp_row, $disp_col)\n";
                            die "Parsing field name erroe: field name missing @ ($disp_row, $disp_col)";
                        }

                        $reg_field_cell = $worksheet->get_cell ($row, $reg_field_col);
                        if (!$reg_field_cell){
                            next;
                        }
                        $reg_field_val = $reg_field_cell->value;

                        # Parsing field bit
                        if ($reg_field_val =~ /\[\s*(\d+)\s*\:\s*(\d+)\s*\]/){
                            $field_start_addr = $1;
                            $field_end_addr = $2;
                            if ( $last_field_end_addr != - 1 ) {
                                if($last_field_end_addr <= $field_start_addr){
                                    cov2disp ($row, $reg_field_col);
                                    print RB_LOG_FH "Parsing field error: bit $field_start_addr is overlapped @ ($disp_row, $disp_col).\n";
                                    die "Parsing field error: bit $field_start_addr is overlapped @ ($disp_row, $disp_col).";
                                }
                                elsif ( ($last_field_end_addr-$field_start_addr) > 1){
                                    cov2disp ($row, $reg_field_col);
                                    my $tmp = $last_field_end_addr-1;
                                    print RB_LOG_FH "Parsing field error: bit $tmp is missing @ ($disp_row, $disp_col).\n";
                                    die "Parsing field error: bit $tmp is missing @ ($disp_row, $disp_col).";
                                }
                            }
                            else{
                                if($field_start_addr != $reg_size_val-1){
                                    cov2disp($row, $reg_field_col);
                                    print RB_LOG_FH "Parsing field error: the MSB is invalid @ ($disp_row, $disp_col).\n";
                                    die "Parsing field error: the MSB is invalid @ ($disp_row, $disp_col)";
                                }
                            }
                            $last_field_start_addr = $field_start_addr;
                            $last_field_end_addr = $field_end_addr;

                            $field_width = $field_start_addr-$field_end_addr+1;
                            if ($field_width <= 0 || $field_width > 32){
                                cov2disp ($row, $reg_field_col);
                                print RB_LOG_FH "Parsing field width error @ ($disp_row, $disp_col).\n";
                                die "Parsing field width error @ ($disp_row, $disp_col).";
                            }
                            #print "Found a field($reg_field_val $field_start_addr $field_width)\n";
                        }
                        elsif ($reg_field_val =~ /\[\s*(\d+)\s*\]/){
                            $field_start_addr = $1;
                            $field_end_addr = $1;

                            if ( $last_field_end_addr != - 1 ) {
                                if( $last_field_end_addr <= $field_start_addr){

                                    cov2disp ($row, $reg_field_col);
                                    print RB_LOG_FH "Parsing field error: bit $field_start_addr is overlapped @ ($disp_row, $disp_col).\n";
                                    die "Parsing field error: bit $field_start_addr is overlapped @ ($disp_row, $disp_col).";
                                }
                                elsif ( ( $last_field_end_addr-$field_start_addr ) > 1){
                                    cov2disp ($row, $reg_field_col);
                                    my $tmp = $last_field_end_addr-1;
                                    print RB_LOG_FH "Parsing field error: bit $tmp is missing @ ($disp_row, $disp_col).\n";
                                    die "Parsing field error: bit $tmp is missing @ ($disp_row, $disp_col).";
                                }
                            }
                            else{
                                if($field_start_addr != $reg_size_val-1){
                                    cov2disp($row, $reg_field_col);
                                    print RB_LOG_FH "Parsing field error: the MSB is invalid @ ($disp_row, $disp_col)\n";
                                    die "Parsing field error: the MSB is invalid @ ($disp_row, $disp_col)";
                                }
                            }
                            $last_field_start_addr = $field_start_addr;
                            $last_field_end_addr = $field_end_addr;

                            $field_width = 1;
                            #print "Found a field($reg_field_val $field_start_addr $field_width)\n";
                        }
                        else{
                            cov2disp($row, $reg_field_col);
                            print RB_LOG_FH "Parsing field error: field format invalid @ ($disp_row, $disp_col)\n";
                            die "Parsing field error: field format invalid @ ($disp_row, $disp_col)";
                        }

                        # Parsing field access property
                        $reg_field_access_cell = $worksheet->get_cell ($row, $reg_field_access_col);
                        if(!$reg_field_access_cell){
                            next;
                        }
                        $reg_field_access_val = $reg_field_access_cell->value;
                        $reg_field_access_val =~ s/\s//g;

                        if ($reg_field_access_val =~ /^RW$/i)
                        {
                            $reg_field_access_val = "read-write";
                        }
                        elsif ($reg_field_access_val =~ /^RO$/i)
                        {
                            $reg_field_access_val = "read-only";
                        }
                        elsif ( $reg_field_access_val =~ /^WO$/i )
                        {
                            $reg_field_access_val = "write-only";
                        }
                        elsif ($reg_field_access_val =~ /^RC$/i)
                        {
                            $reg_field_access_val = "read-clear";
                        }
                        elsif ($reg_field_access_val =~ /^WC$/i)
                        {
                            $reg_field_access_val = "write-clear";
                        }
                        elsif ($reg_field_access_val eq ''){
                            cov2disp ($row, $reg_field_access_col);
                            print RB_LOG_FH "Parsing error: field access missing @ ($disp_row, $disp_col).\n";
                            die "Parsing error: field access missing @ ($disp_row, $disp_col).";
                        }
                        else {
                            cov2disp ($row, $reg_field_access_col);
                            print RB_LOG_FH "Parsing error: field access type don't support @ ($disp_row, $disp_col).\n";
                            die "Parsing error: field access type don't support @ ($disp_row, $disp_col).";
                        }

                        if(($reg_field_name_val =~ /reserved/i) && ($reg_field_access_val ne 'read-only')){
                            cov2disp ($row, $reg_field_access_col);
                            print RB_LOG_FH "Parsing error: the reserved field access property must be read-only @ ($disp_row, $disp_col).\n";
                            die "Parsing error: the reserved field access property must be read-only @ ($disp_row, $disp_col).";
                        }

                        # Parsing register reset value
                        $reg_reset_cell = $worksheet->get_cell ($row, $reg_reset_col);
                        if (!$reg_reset_cell){
                            next;
                        }
                        $reg_reset_val = $reg_reset_cell->value;
                        chomp ($reg_reset_val);
                        $reg_reset_val =~ s/\s//g;

                        my $reset_val_vld=0;
                        my $field_reset=0;

                        if ($reg_reset_val =~ /(\d+)'h([\da-fA-F]+)/){
                            if ($1 != $field_width){
                                cov2disp ($row, $reg_field_col);
                                print RB_LOG_FH "Parsing field width error @ ($disp_row, $disp_col).\n";
                                die "Parsing field width error @ ($disp_row, $disp_col).";
                            }

                            if ($reg_field_name_val !~ /reserved/i){
                                $field_reset = hex ($2);
                                $reset_val_vld = 1;
                            }
                            else{
                                $reset_val_vld = 0;
                            }

                        }
                        elsif ($reg_reset_val =~ /(\d+)'d(\d+)/){
                            if ($1 != $field_width){
                                cov2disp ($row, $reg_field_col);
                                print RB_LOG_FH "Parsing field width error @ ($disp_row, $disp_col).\n";
                                die "Parsing field width error @ ($disp_row, $disp_col).";
                            }

                            if ($reg_field_name_val !~ /reserved/i){

                                $field_reset = $2;
                                $reset_val_vld = 1;
                            }
                            else{
                                $reset_val_vld = 0;
                            }
                        }
                        elsif ($reg_reset_val =~ /(\d+)/){
                            if ($reg_field_name_val !~ /reserved/i){
                                $field_reset = $1;
                                $reset_val_vld = 1;
                            }
                        }
                        elsif ($reg_reset_val eq ''){
                            if ($reg_field_name_val !~ /reserved/i){
                                $reset_val_vld = 0;
                                cov2disp ($row, $reg_reset_col);
                                print RB_LOG_FH "Parsing field error: reset value can't be empty @ ($disp_row, $disp_col).\n";
                                die "Parsing field error: reset value can't be empty @ ($disp_row, $disp_col).";
                            }
                        }
                        else{
                            cov2disp ($row, $reg_reset_col);
                            print RB_LOG_FH "Parsing field reset value error @ ($disp_row, $disp_col).\n";
                            die "Parsing field reset value error @ ($disp_row, $disp_col).";
                        }

                        if ($reset_val_vld){
                            $reset_val = $reset_val | ($field_reset << $field_end_addr);
                            my $field_mask = 0;
                            for (0 .. ( $field_width - 1 )){
                                $field_mask = $field_mask | ( 1 << $_ );
                                #printf "field_mask=%x\n", $field_mask;
                            }
                            $mask_val = $mask_val | ( $field_mask << $field_end_addr);
                            #print "reset val: $reset_val\n";
                            #printf "mask val=%x\n", $mask_val;
                        }

                        # Parsing register field name
                        if ($reg_field_name_val !~ /reserved/i){
                            my $field_name = "fld_"."$reg_field_name_val";
                            $register{"$field_name"} = "$field_name, $field_end_addr, $field_width, $reg_field_access_val";
                            push @field_array, $register{$field_name};
                        }
                        else{
                            my $field_name = "reserved_$field_end_addr";
                            $register{"$field_name"} = "$field_name, $field_end_addr, $field_width, $reg_field_access_val";
                            push @field_array, $register{$field_name};
                        }


                        # Parsing register descriptrion
                        $reg_dsc_cell = $worksheet->get_cell($row, $reg_dsc_col);
                        if(!$reg_dsc_cell){
                            $reg_dsc_val = "$reg_field_name_val";
                        }
                        else{
                            $reg_dsc_val = $reg_dsc_cell->value;
                        }

                        if($reg_dsc_val =~ /^\s*$/){
                            $reg_dsc_val = $reg_field_name_val;
                        }

                        #if($reg_field_name_val !~ /reserved/i) {
                        #    my $fld_dsc_name = "flddsc_"."$reg_field_name_val";
                        #    $register{"$fld_dsc_name"} = $reg_dsc_val;
                        #}
                        push @field_dsc_array, $reg_dsc_val;

                        if ($field_end_addr == 0){
                            $reset_val = sprintf "0x%x", $reset_val;
                            $mask_val = sprintf "0x%x", $mask_val;
                            $register{"reset"} = "$reset_val, $mask_val";
                            my @tmp_field_array = @field_array;
                            $register{"sorted_field_array"} = \@tmp_field_array;
                            my @tmp_field_dsc_array = @field_dsc_array;
                            $register{"sorted_field_dsc_array"} = \@tmp_field_dsc_array;

                            my %tmp_reg = %register;
                            #push @reg_ar, \%tmp_reg;
                            if (exists $reg_ar_by_name{$register{name}}){
                                cov2disp($row, 0);
                                printf "Register: $register{name} has exists already @ ($disp_row, $disp_col)!\n";
                                die "Register: $register{name} has exists already @ ($disp_row, $disp_col)!";
                            }

                            if (exists $reg_ar_by_offset{$register{addressOffset}}){
                                cov2disp($row, 0);
                                printf RB_LOG_FH "Register: $register{addressOffset} has exists already @ ($disp_row, $disp_col)!\n";
                                die "Register: $register{addressOffset} has exists already @ ($disp_row, $disp_col)!";
                            }

                            $reg_ar_by_name{$register{name}} = \%tmp_reg;
                            $reg_ar_by_offset{$register{addressOffset}} = \%tmp_reg;

                            $reg_is_parsing = 0;
                            $extr_reg_num += 1;
                        }
                    }
                }
                else{
                    cov2disp ($row, $reg_offset_col);
                    #printf "Cell ($disp_row, $disp_col)=[$reg_offset_val] is a invalid offset, check it!\n";
                }
            }
        }

        # Print a warning if any data was truncated to fit the cells.
        #print "* Note: Some data truncated to fit cells.\n" if $data_truncated;
        #exit 1;
    }
    print RB_LOG_FH "Register parsing done, $extr_reg_num register has been extracted!\n";
    print "Register parsing done, $extr_reg_num register has been extracted!\n";
}#}}}

my $reg_tbl_fn;;
my $REG_TBL_FH;

sub print_register_array{#{{{
    my @reg_keys = sort keys %reg_ar_by_offset;
    
    print RB_LOG_FH "reg table generating...\n";
    print "reg table generating...\n";
    $reg_tbl_fn = lc("$gen_dir/reg_tbl.txt");
    open $REG_TBL_FH, ">$reg_tbl_fn" || die "Can't open file: $reg_tbl_fn";

    print $REG_TBL_FH "\n\n";
    print $REG_TBL_FH "//**********************************************\n";
    print $REG_TBL_FH "//* register array print: \n";
    print $REG_TBL_FH "//**********************************************\n";
    print $REG_TBL_FH "\n\n";

    print $REG_TBL_FH "REG NUM: ".@reg_keys."\n\n";
    #foreach (@reg_ar){
    foreach (@reg_keys){
        #print_register (%{$_}) ;
        print_register (%{$reg_ar_by_offset{$_}});
    }

    close $REG_TBL_FH;
    print RB_LOG_FH "reg table output file: $reg_tbl_fn\n";
    print "reg table output file: $reg_tbl_fn\n";
}#}}}

sub print_register{#{{{
    my %my_reg = @_;
    my @tmp_fld_ar = @{$my_reg{sorted_field_array}};
    my @tmp_fld_dsc_ar = @{$my_reg{sorted_field_dsc_array}};

    my $loop;

    #use encoding 'utf8', REG_TBL_FH=>'euc_cn';

    binmode $REG_TBL_FH, ':encoding(utf8)';

    print $REG_TBL_FH "  [REG: $my_reg{name}]\n";
    #foreach (sort keys %my_reg){
    #    print $REG_TBL_FH "    $_ -> $my_reg{$_}\n";
    #}

    #select STDOUT;
    #select $REG_TBL_FH;

    my $field_num = $#tmp_fld_ar+1;

    print $REG_TBL_FH "    [addressOffset] $my_reg{addressOffset}\n";
    print $REG_TBL_FH "    [reg short dsc] $my_reg{reg_short_dsc}\n";
    print $REG_TBL_FH "    [size] $my_reg{size}\n";
    print $REG_TBL_FH "    [reset] $my_reg{reset}\n";
    print $REG_TBL_FH "    [reg dsc] $my_reg{reg_dsc}\n";
    print $REG_TBL_FH "    [reg dsc end]\n";
    print $REG_TBL_FH "    [field num: $field_num]\n";

    #foreach(sort keys %my_reg){
    #    my $tmp_fld_name;
    #    if($_ =~ /^fld_/){
    #        $tmp_fld_name = $_;
    #        $tmp_fld_name =~ s/^fld_//g;
    #        print $REG_TBL_FH "    [field: $tmp_fld_name] $my_reg{$_}\n";
    #    }
    #    elsif($_ =~ /^flddsc_/){
    #        $tmp_fld_name = $_;
    #        $tmp_fld_name =~ s/^flddsc_//g;
    #        print $REG_TBL_FH "    [field dsc: $tmp_fld_name] $my_reg{$_}\n";
    #        print $REG_TBL_FH "    [field dsc end]\n";
    #    }
    #    elsif($_ =~ /^reserved_(\d+)/){
    #        print $REG_TBL_FH "    [field: reserved $1] $my_reg{$_}\n";
    #    }
    #}
    $loop = 0;
    foreach (reverse @tmp_fld_ar){
        print $REG_TBL_FH "    [field: $loop] $_\n";
        $loop += 1;
    }
    $loop = 0;
    foreach (reverse @tmp_fld_dsc_ar){
        print $REG_TBL_FH "    [field dsc: $loop] $_\n";
        $loop += 1;
        print $REG_TBL_FH "    [field dsc end]\n";
    }

    print $REG_TBL_FH "  [REG END]\n\n";

}#}}}

sub cov2disp(){#{{{
    my ($tmp_row, $tmp_col) = ($_[0], $_[1]);
    $disp_row = $tmp_row+1;
    $disp_col = sprintf "%c", 65+$tmp_col;
}#}}}

sub cal_str_len(){#{{{
    my $str = $_[0];
    my $len = rindex $str."\$", "\$";
    return $len;
}#}}}

###############################################################################
# rgm_generator().
#   generator xml file for rgm
###############################################################################
my $RGM_GEN_FH;
my $rgm_gen_file;

sub rgm_gen{#{{{
    $rgm_gen_file = lc($gen_dir."/".$mem_map."_".$module_name.".spirit");
    printf RB_LOG_FH "rgm generating...\n";
    printf "rgm generating...\n";
    open RGM_GEN_FH, ">$rgm_gen_file" || die "Can't open file: $rgm_gen_file";
    block_gen (%rgm_block);
    close RGM_GEN_FH;
    printf RB_LOG_FH "rgm output file: $rgm_gen_file\n";
    printf "rgm output file: $rgm_gen_file\n";
}#}}}

sub block_gen{#{{{
    my %block = @_;
    my $name = $block{name};
    my $baseAddress = $block{baseAddress};
    my $range = $block{range};
    my $width = $block{width};
    #&block_hir_print (%block) ;
printf RGM_GEN_FH << "msg";
<?xml version="1.0"?>
<spirit:component xmlns:spirit="http://www.spiritconsortium.org/XMLSchema/SPIRIT/1.4"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="\$REGMEM_HOME/builder/ipxact/schema/ipxact
                                        \$REGMEM_HOME/builder/ipxact/schema/ipxact/index.xsd">
    <spirit:vendor>spreadtrum</spirit:vendor>
    <spirit:library>$prj</spirit:library>
    <spirit:name>rgm_vrf_model</spirit:name>
    <spirit:version>1.0</spirit:version>
    <spirit:memoryMaps>
        <spirit:memoryMap>
            <spirit:name>$mem_map</spirit:name>
            <spirit:addressBlock>
                <spirit:name>$name</spirit:name>
                <spirit:baseAddress>$baseAddress</spirit:baseAddress>
                <spirit:range>$range</spirit:range>
                <spirit:width>$width</spirit:width>
msg

    foreach (sort keys %block)
    {
        if ($_ =~ /^reg_/)
        {
            #printf "\n\n\t\t$_\n";
            &register_gen (%{$block{$_}});
        }
    }

printf RGM_GEN_FH << "msg";
            </spirit:addressBlock>
        </spirit:memoryMap>
    </spirit:memoryMaps>
</spirit:component>
msg
}#}}}

sub register_gen{#{{{
    my %reg = @_;
    my $name = $reg{name};
    my $addressOffset = $reg{addressOffset};
    my $size = $reg{size};
    #my $access = $reg{access};
    my ( $value, $mask ) = split ",", $reg{reset};
    $value =~ s/\s//g;
    $mask =~ s/\s//g;

printf RGM_GEN_FH << "msg";
                <spirit:register>
                    <spirit:name>$name</spirit:name>
                    <spirit:description>Register $name define</spirit:description>
                    <spirit:addressOffset>$addressOffset</spirit:addressOffset>
                    <spirit:size>$size</spirit:size>
                    <spirit:reset>
                        <spirit:value>$value</spirit:value>
                        <spirit:mask>$mask</spirit:mask>
                    </spirit:reset>
msg

    foreach (sort keys %reg)
    {
        if ($_ =~ /^fld_/)
        {
            my ( $name, $bitOffset, $bitWidth, $fld_access) = split ",", $reg{$_};
            $name =~ s/\s//g;
            $bitOffset =~ s/\s//g;
            $bitWidth =~ s/\s//g;
            $fld_access =~ s/\s//g;

printf RGM_GEN_FH << "msg";
                    <spirit:field>
                        <spirit:name>$name</spirit:name>
                        <spirit:bitOffset>$bitOffset</spirit:bitOffset>
                        <spirit:bitWidth>$bitWidth</spirit:bitWidth>
                        <spirit:access>$fld_access</spirit:access>
                    </spirit:field>
msg
        }
    }
printf RGM_GEN_FH << "msg";
                </spirit:register>
msg
}#}}}

sub block_hir_print{#{{{
    my %block = @_;
    print "\n\n";
    print "**********************************************\n";
    print "* block hir print: \n";
    print "**********************************************\n";
    print "  BLOCK_NAME: $block{name}\n";
    foreach (sort keys %block)
    {
        #my $tmp_reg=$_;
        if ($_ =~ /^reg_/)
        {
            print "  $_\n";
            my %tmp_reg = %{$block{$_}};
            foreach (keys %tmp_reg)
            {
                print "    $_ -> $tmp_reg{$_}\n";
            }
        }
    }
}#}}}

sub rgm_block_build{#{{{
    my @reg_keys = keys %reg_ar_by_offset;

    $rgm_block{name} = $module_name;
    $rgm_block{baseAddress} = $module_base_addr;
    $rgm_block{range} = $module_mem_range;
    $rgm_block{width} = 32;

    foreach (@reg_keys){
        my $reg_name = "reg_$_";
        $rgm_block{$reg_name} = \%{$reg_ar_by_offset{$_}};
    }
}#}}}

###############################################################################
# register define and wr task generator.
#   generator register define and w/r tasks for testbench.
###############################################################################
my $vlog_def_fn;
my $vlog_task_fn;
my $c_dfe_fn;
my $c_fun_fn;

sub vlog_reg_def_gen{ #{{{
    my $reg_base_macro;
    my $reg_def_macro;
    my $reg_def_off;
    my $reg_def_val;

    $vlog_def_fn = lc($gen_dir."/".$mem_map."_".$module_name."_reg_def.sv");
    printf RB_LOG_FH "vlog reg define generating...\n";
    printf "vlog reg define generating...\n";
    open VLOG_DEF_FH, ">$vlog_def_fn" || die "Can't open file: $vlog_def_fn";

    printf VLOG_DEF_FH << "msg";
//------------------------------------------------------------------------------
// This is a auto-generated file. Generated by $0.
// Input file: $input_fn
// User: $user 
// Date: $date @ $time
//------------------------------------------------------------------------------

msg

    $reg_base_macro = uc($mem_map."_".$module_name."_REG_BASE");
    $reg_base_macro =~ s/0x/32'h/;
    printf VLOG_DEF_FH "`define $reg_base_macro $rgm_block{baseAddress}\n";
    printf VLOG_DEF_FH "\n";
    foreach(sort keys %rgm_block){
        if($_ =~ /^reg_/){
            $reg_def_macro = uc("REG_${$rgm_block{$_}}{name}");
            $reg_def_off = ${$rgm_block{$_}}{addressOffset};
            $reg_def_off =~ s/0x/32'h/g;
            $reg_def_val = "(`$reg_base_macro\+$reg_def_off)";
            printf VLOG_DEF_FH "`define $reg_def_macro $reg_def_val\n";
        }
    }

    close VLOG_DEF_FH;
    printf RB_LOG_FH "vlog reg define output file: $vlog_def_fn\n";
    printf "vlog reg define output file: $vlog_def_fn\n";


} #}}}

sub vlog_reg_task_gen{#{{{
    my $reg_name;
    my $reg_width;
    my $fld_name;
    my $fld_width;
    my $fld_bit_offset;
    my $fld_acs;
    my $fld_msb;

    my $reg_def_off;
    my $reg_dsc;

    $vlog_task_fn = lc($gen_dir."/".$mem_map."_".$module_name."_reg_task.sv");
    printf RB_LOG_FH "vlog reg tast generating...\n";
    printf "vlog reg tast generating...\n";
    open VLOG_TASK_FH, ">$vlog_task_fn" || die "Can't open file: $vlog_task_fn";

    binmode VLOG_TASK_FH, ':encoding(utf8)';

    print VLOG_TASK_FH << "msg";
//------------------------------------------------------------------------------
// This is a auto-generated file. Generated by $0.
// Input file: $input_fn
// User: $user 
// Date: $date @ $time
//------------------------------------------------------------------------------

msg


    foreach(sort keys %rgm_block){
        if($_ =~ /^reg_/){
            my %reg = %{$rgm_block{$_}};
            my @tmp_fld_ar = @{$reg{sorted_field_array}};
            my @tmp_fld_dsc_ar = @{$reg{sorted_field_dsc_array}};
            my $loop;

            $reg_name = uc($reg{name});
            $reg_width = $reg{size}-1;
            $reg_def_off = $reg{addressOffset};
            $reg_def_off =~ s/0x/32'h/g;
            $reg_dsc = $reg{reg_short_dsc};
            print VLOG_TASK_FH << "msg";

//------------------------------------------------------------------------------
// Auto-generated egister write/read tasks.
// Regiser name: $reg_name
// Regiser offset: $reg_def_off
// Regiser description: $reg_dsc
//------------------------------------------------------------------------------
task wr_$reg_name(input [$reg_width:0] wdata);
begin
    `reg_write(`REG_$reg_name, wdata);
end
endtask

task rd_$reg_name(output [$reg_width:0] rdata);
begin
    `reg_read(`REG_$reg_name, wdata);
end
endtask

msg
            
            #foreach(sort keys %reg){
            $loop = 0;
            foreach(@tmp_fld_ar){
                if($_ =~ /^fld_/){
                    my $tmp_fld_dsc;
                    my $mul_spc = " " x 4;
                    #($fld_name, $fld_bit_offset, $fld_width, $fld_acs) = split ",", $reg{$_};              
                    ($fld_name, $fld_bit_offset, $fld_width, $fld_acs) = split ",", $_;              
                    $fld_name =~ s/\s//g;
                    $fld_bit_offset =~ s/\s//g;
                    $fld_width =~ s/\s//g;
                    $fld_acs =~ s/\s//g;

                    $fld_width = $fld_width-1;
                    $fld_msb = $fld_width+$fld_bit_offset;

                    $tmp_fld_dsc = $tmp_fld_dsc_ar[$loop];
                    $tmp_fld_dsc = join "\n//$mul_spc", split "\n", $tmp_fld_dsc;
                    #$tmp_fld_dsc = encode("gb2312", $tmp_fld_dsc);
                    #exit;

                    print VLOG_TASK_FH << "msg";

//------------------------------------------------------------------------------
// Auto-generated register fields write/read tasks.
// Register name: $reg_name 
// Field name: $fld_name
// Field bits: [$fld_msb : $fld_bit_offset]
// Field description: 
//    $tmp_fld_dsc 
//------------------------------------------------------------------------------
task wr_$reg_name\_$fld_name(input [$fld_width:0] wdata);
reg [$reg_width:0] tmp;
begin
    `reg_read(`REG_$reg_name, tmp);
    tmp[$fld_msb:$fld_bit_offset] = wdata[$fld_width:0];
    `reg_write(`REG_$reg_name, tmp);
end
endtask

task rd_$reg_name\_$fld_name(output [$fld_width:0] rdata);
reg [$reg_width:0] tmp;
begin
    `reg_read(`REG_$reg_name, tmp);
    rdata[$fld_width:0] = tmp[$fld_msb:$fld_bit_offset];
end
endtask

msg
                } #if($_ =~ /^fld_/){
                $loop += 1;
            }#for
        }
    }

    close VLOG_TASK_FH;
    printf RB_LOG_FH "vlog reg task output file: $vlog_task_fn\n";
    printf "vlog reg task output file: $vlog_task_fn\n";
}#}}}

sub c_reg_def_gen{#{{{

}#}}}



#}}}


###############################################################################


#{{{

###############################################################################
# Copyright (c) 2011 @ Spreadtrum Communication Inc.
# Spreadtrum Confidential Properity. All rights are reserved.
#
# File name   : reg_manual_gen.pl
# Author      : junhao.zheng@spreadtrum.com
# Date        : 2011/05/04
# Description : read "reg_tbl.txt" and generate the register manual document.
#                - Register table for doc generator. (reg_tbl.txt) 
#                - Microsoft Word Document.          (reg_manual.doc) 
#
###############################################################################

use strict;
use warnings;

use Text::Wrap;
use Win32::OLE qw(in with);
use Win32::OLE::Const 'Microsoft Word';
use Cwd;
use utf8;
$Win32::OLE::Warn = 3;

my ($tmp, $tmp_s, @tmp_arr) ;
my ($cnt, $row, $col);
my ($word, $document, $tables, $table, $range, $cell, $selection, $reg) ;
my ($tot_line, $fline, $doc_end) ;
my ($REG_TOT, @REG_ARR, $reg_cnt, $fld_cnt, $table_cnt) ;

sub reg_manual_gen_main($$) {
    my ($reg_tbl_fname, $cur_path) = @_ ;

    print           sprintf("\nReg_manual: %s, %s\n", $reg_tbl_fname, $cur_path) ;
    print RB_LOG_FH sprintf("\nReg_manual: %s, %s\n", $reg_tbl_fname, $cur_path) ;

    #$cur_path = getcwd ;
    #my $in_fname = $ARGV[0] ;
    #my $reg_tbl_fname = "$cur_path\\$ARGV[0]" ;

    open (REG_TABLE_F, "<$reg_tbl_fname") || die "Can't open $reg_tbl_fname for reading\n" ;
    binmode REG_TABLE_F, ':encoding(utf8)';

    $reg_cnt = 0 ;

    # parse REG_TABLE_FILE {{{
    print           "\n===== RegTable Parsing ...\n" ;
    print RB_LOG_FH "\n===== RegTable Parsing ...\n" ;
    while ($fline = <REG_TABLE_F>) {
        next if ($fline =~ /\/\//) ;
        next if ($fline =~ /^\s*$/) ;
        chomp $fline ;
        if ($fline =~ /REG NUM/) {
            @tmp_arr = $fline =~ /(\d+)/ ;
            $REG_TOT = $tmp_arr[0] ;
        } elsif ($fline =~ /^\s+\[REG:/) {
            @tmp_arr = $fline =~ /\[REG: (\S+)\]/ ;
            $REG_ARR[$reg_cnt]{name} = $tmp_arr[0] ;
            print           sprintf("%2d: %-20s ", $reg_cnt, $REG_ARR[$reg_cnt]{name}) ;
            print RB_LOG_FH sprintf("%2d: %-20s ", $reg_cnt, $REG_ARR[$reg_cnt]{name}) ;
        } elsif ($fline =~ /^\s+\[addressOffset\]/) {
            @tmp_arr = $fline =~ /0x(\S+)/ ;
            $REG_ARR[$reg_cnt]{addrOffset} = hex($tmp_arr[0]) ;
            print           sprintf("[0x%04x] ", $REG_ARR[$reg_cnt]{addrOffset}) ;
            print RB_LOG_FH sprintf("[0x%04x] ", $REG_ARR[$reg_cnt]{addrOffset}) ;
        } elsif ($fline =~ /^\s+\[reset\]/) {
            $fline =~ s/,/ /g ;
            @tmp_arr = $fline =~ /0x(\S+)/g ;
            $REG_ARR[$reg_cnt]{resetVal} = hex($tmp_arr[0]) ;
            print           sprintf(" 0x%08x\n", $REG_ARR[$reg_cnt]{resetVal}) ;
            print RB_LOG_FH sprintf(" 0x%08x\n", $REG_ARR[$reg_cnt]{resetVal}) ;
        } elsif ($fline =~ /^\s+\[reg short dsc\]/) {
            @tmp_arr = $fline =~ /^\s+\[.*\] (\S+)/ ;
            $REG_ARR[$reg_cnt]{shortDesc} = $tmp_arr[0] ;
        } elsif ($fline =~ /^\s+\[reg dsc\]/) {
            @tmp_arr = $fline =~ /^\s+\[reg dsc\] (.*)/ ;
            $REG_ARR[$reg_cnt]{reg_desc} = "$tmp_arr[0]\n" ;
            REG_DSC: while ($fline = <REG_TABLE_F>) {
                if ($fline =~ /^\s+\[reg dsc end\]/) {
                    #print "$REG_ARR[$reg_cnt]{reg_desc}" ;
                    last REG_DSC ;
                } else {
                    $REG_ARR[$reg_cnt]{reg_desc} .= "$fline" ;
                }
            }
        } elsif ($fline =~ /^\s+\[field num:/) {
            @tmp_arr = $fline =~ /(\d+)/ ;
            $REG_ARR[$reg_cnt]{fld_cnt} = $tmp_arr[0] ;
        } elsif ($fline =~ /^\s+\[field:/) {
            @tmp_arr = $fline =~ /^\s+\[field: (\d+)/ ;
            $tmp = $tmp_arr[0] ;
            $fline =~ s/^\s+\[field: .*\] +// ;
            @tmp_arr = split (/,/, $fline) ;
            $tmp_s = $tmp_arr[0] ;
            $tmp_s =~ s/^fld_// ;
            $tmp_s =~ s/^(reserved)_\d+/$1/ ;
            $REG_ARR[$reg_cnt]{fld}[$tmp]{name} = $tmp_s ;
            $REG_ARR[$reg_cnt]{fld}[$tmp]{sp}   = $tmp_arr[1] ;
            $REG_ARR[$reg_cnt]{fld}[$tmp]{ep}   = $tmp_arr[1] + $tmp_arr[2] - 1 ;
            $REG_ARR[$reg_cnt]{fld}[$tmp]{typ}  = $tmp_arr[3] ;
            print           sprintf("    %2d. %-16s [%2d :%2d] %-10s\n", $tmp, $REG_ARR[$reg_cnt]{fld}[$tmp]{name}, $REG_ARR[$reg_cnt]{fld}[$tmp]{ep}, $REG_ARR[$reg_cnt]{fld}[$tmp]{sp}, $REG_ARR[$reg_cnt]{fld}[$tmp]{typ}) ;
            print RB_LOG_FH sprintf("    %2d. %-16s [%2d :%2d] %-10s\n", $tmp, $REG_ARR[$reg_cnt]{fld}[$tmp]{name}, $REG_ARR[$reg_cnt]{fld}[$tmp]{ep}, $REG_ARR[$reg_cnt]{fld}[$tmp]{sp}, $REG_ARR[$reg_cnt]{fld}[$tmp]{typ}) ;
        } elsif ($fline =~ /^\s+\[field dsc:/) {
            @tmp_arr = $fline =~ /^\s+\[field dsc: (\d+)/ ;
            $tmp = $tmp_arr[0] ;
            @tmp_arr = $fline =~ /^\s+\[field dsc: .*\] (.*)/ ;
            $REG_ARR[$reg_cnt]{fld}[$tmp]{desc} = "$tmp_arr[0]\n" ;
            FLD_DSC: while ($fline = <REG_TABLE_F>) {
                if ($fline =~ /^\s+\[field dsc end\]/) {
                    #print "        $tmp: $REG_ARR[$reg_cnt]{fld}[$tmp]{desc}" ;
                    last FLD_DSC ;
                } else {
                    $REG_ARR[$reg_cnt]{fld}[$tmp]{desc} .= "$fline" ;
                }
            }
        } elsif ($fline =~ /^\s+\[field dsc end\]/) {
        } elsif ($fline =~ /^\s+\[REG END\]/) {
            $reg_cnt ++ ;
        } else {
        }
    }

    close(REG_TABLE_F) ;
    #}}}

    #my @reg_arr = ( 
    #    { name => "reg1", addr_offset => 28,  fld0 => [0, 1, 2, "W", "ena"], fld1 => [2, 31, 0, "NA", "reserved"]},
    #    { name => "reg2", addr_offset => 32,  },
    #) ;

    print           "\n===== Word Writing ...\n" ;
    print RB_LOG_FH "\n===== Word Writing ...\n" ;

    $word = Win32::OLE->new('Word.Application') || die "CreateObject: ".Win32::OLE->LastError;
    $word->{Visible} = 0;

    $document = $word->Documents->Add();
    $word->Selection->Font->{Name} = "Arial";
    $word->Selection->Font->{Size} = 10;
    #$word->Selection->Font->{Animation} = 3;
    $word->Selection->TypeText("\nRegister Manual\n\n");

    $tables = $document->Tables;

    $table_cnt = 1 ;

    $word->Selection->TypeParagraph;
    $word->Selection->TypeText("\nRegister Address Map\n");
    $word->Selection->TypeText("ARM base address:\n");
    $word->Selection->TypeText("DSP base address:\n");

    $document->Tables->add($word->Selection->Range, $REG_TOT+1, 3);
    $table = $document->Tables($table_cnt);
    $table->Select();

    &get_table_map($table) ;

    $table_cnt ++ ;

    $doc_end = $document->GoTo(wdGoToLine, wdGoToLast); 
    $doc_end->InsertBefore("\n"); 
    $doc_end = $document->GoTo(wdGoToLine, wdGoToLast); 
    $doc_end->Select; 

    for ($reg_cnt = 0 ; $reg_cnt < $REG_TOT ; $reg_cnt ++) {
        $word->Selection->TypeParagraph;
        $tmp = sprintf("%s\n", $REG_ARR[$reg_cnt]{name}) ;
        print           sprintf("%2d: 0x%04x %-20s \n", $reg_cnt, $REG_ARR[$reg_cnt]{addrOffset}, $REG_ARR[$reg_cnt]{name}) ;
        print RB_LOG_FH sprintf("%2d: 0x%04x %-20s \n", $reg_cnt, $REG_ARR[$reg_cnt]{addrOffset}, $REG_ARR[$reg_cnt]{name}) ;
        $word->Selection->TypeText($tmp);
        $document->Tables->add($word->Selection->Range, 9, 17);
        $table = $document->Tables($table_cnt);
        $table->Select();
        $tot_line = &gen_table_bit($table, $reg_cnt) ;
        $table_cnt ++ ;

        $doc_end = $document->GoTo(wdGoToLine, wdGoToLast); 
        $doc_end->InsertBefore("\n"); 
        $doc_end = $document->GoTo(wdGoToLine, wdGoToLast); 
        $doc_end->Select; 

        $tmp = $REG_ARR[$reg_cnt]{fld_cnt} + 1 ;
        $document->Tables->add($word->Selection->Range, $tmp, 5);
        $table = $document->Tables($table_cnt);
        $table->Select();
        $tot_line = &gen_table_details($table, $reg_cnt) ;
        $table_cnt ++ ;

        $doc_end = $document->GoTo(wdGoToLine, wdGoToLast); 
        $doc_end->InsertBefore("\n"); 
        $doc_end = $document->GoTo(wdGoToLine, wdGoToLast); 
        $doc_end->Select; 
    }

    $word->Selection->TypeParagraph;
    $word->Selection->TypeText("All End\n\n");

    print           "\nDoc path: ${cur_path}\\reg_manual.doc\n";
    print RB_LOG_FH "\nDoc path: ${cur_path}\\reg_manual.doc\n";
    $document->SaveAs("$cur_path\\reg_manual.doc");
    $document->{Saved} = 1;

    $word->Quit();
}

#----------------------------------------------------------------------------------------------------

sub get_table_map ($) { #{{{
    my ($table) = @_ ;
    my ($reg_cnt) ;

    $table->Borders(wdBorderLeft)      ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderLeft)      ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderRight)     ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderRight)     ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderTop)       ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderTop)       ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderBottom)    ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderBottom)    ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderHorizontal)->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderHorizontal)->{LineWidth} = wdLineWidth100pt  ;
    $table->Borders(wdBorderVertical)  ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderVertical)  ->{LineWidth} = wdLineWidth100pt  ;

    $table->Rows(1)->Borders(wdBorderBottom)->{LineStyle} = wdLineStyleSingle ;
    $table->Rows(1)->Borders(wdBorderBottom)->{LineWidth} = wdLineWidth150pt  ;

    $table->Cell(1,1)->Range->{Text} = "Offset Address" ;
    $table->Cell(1,2)->Range->{Text} = "Name" ;
    $table->Cell(1,3)->Range->{Text} = "Description" ;
    $table->Rows(1)->Range->Font->{Bold} = wdToggle;

    for ($reg_cnt = 0 ; $reg_cnt < $REG_TOT ; $reg_cnt ++) {
        $table->Cell($reg_cnt+2,1)->Range->{Text} = sprintf("0x%04X", $REG_ARR[$reg_cnt]{addrOffset}) ;
        $table->Cell($reg_cnt+2,2)->Range->{Text} = $REG_ARR[$reg_cnt]{name} ;
        $table->Cell($reg_cnt+2,3)->Range->{Text} = $REG_ARR[$reg_cnt]{shortDesc} ;
    }

    $table->AutoFitBehavior(wdAutoFitContent) ;

} #}}}

#----------------------------------------------------------------------------------------------------

sub gen_table_bit ($$) { #{{{
    my ($table, $reg_cnt) = @_ ;
    my ($fld_sp, $fld_ep, $fld_name, $fld_typ, $sp0, $sp1, $ep0, $ep1) ;
    my ($cnt, $col, $row, $range, $tot_line, @rbit0, @rbit1, @mpos0, @mpos1, $rcnt0, $rcnt1, $bit) ;

    $tot_line = 0 ;

    $table->Borders(wdBorderLeft)      ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderLeft)      ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderRight)     ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderRight)     ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderTop)       ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderTop)       ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderBottom)    ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderBottom)    ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderHorizontal)->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderHorizontal)->{LineWidth} = wdLineWidth100pt  ;
    $table->Borders(wdBorderVertical)  ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderVertical)  ->{LineWidth} = wdLineWidth100pt  ;

    $table->Rows(2)->Shading->{BackgroundPatternColor} = wdColorLightTurquoise ;
    $table->Rows(6)->Shading->{BackgroundPatternColor} = wdColorLightTurquoise ;

    $table->Columns(1)->Borders(wdBorderRight)->{LineStyle} = wdLineStyleSingle ;
    $table->Columns(1)->Borders(wdBorderRight)->{LineWidth} = wdLineWidth150pt  ;

    $table->Rows(1)->Borders(wdBorderBottom)->{LineStyle} = wdLineStyleSingle ;
    $table->Rows(1)->Borders(wdBorderBottom)->{LineWidth} = wdLineWidth150pt  ;
    $table->Rows(2)->Borders(wdBorderBottom)->{LineStyle} = wdLineStyleSingle ;
    $table->Rows(2)->Borders(wdBorderBottom)->{LineWidth} = wdLineWidth150pt  ;
    $table->Rows(5)->Borders(wdBorderBottom)->{LineStyle} = wdLineStyleSingle ;
    $table->Rows(5)->Borders(wdBorderBottom)->{LineWidth} = wdLineWidth150pt  ;
    $table->Rows(6)->Borders(wdBorderBottom)->{LineStyle} = wdLineStyleSingle ;
    $table->Rows(6)->Borders(wdBorderBottom)->{LineWidth} = wdLineWidth150pt  ;

    $range = $document->Range($table->Cell(1, 1)->Range->{Start}, $table->Cell(1, 3)->Range->{End}) ; $range->Cells->Merge ;
    $range = $document->Range($table->Cell(1, 2)->Range->{Start}, $table->Cell(1,11)->Range->{End}) ; $range->Cells->Merge ;
    $range = $document->Range($table->Cell(1, 3)->Range->{Start}, $table->Cell(1, 6)->Range->{End}) ; $range->Cells->Merge ;

    $table->Cell(1,1)->Range->{Text} = sprintf("0x%04X",      $REG_ARR[$reg_cnt]{addrOffset}) ;
    $table->Cell(1,2)->Range->{Text} = sprintf("%s (0x%08X)", $REG_ARR[$reg_cnt]{shortDesc}, $REG_ARR[$reg_cnt]{resetVal}) ;
    $table->Cell(1,3)->Range->{Text} = sprintf("%s",          $REG_ARR[$reg_cnt]{name}) ;

    $table->Cell(2,1)->Range->{Text} = "Bit"  ;         $table->Cell(6,1)->Range->{Text} = "Bit"  ;
    $table->Cell(3,1)->Range->{Text} = "Name" ;         $table->Cell(7,1)->Range->{Text} = "Name" ;
    $table->Cell(4,1)->Range->{Text} = "Type" ;         $table->Cell(8,1)->Range->{Text} = "Type" ;
    $table->Cell(5,1)->Range->{Text} = "Reset";         $table->Cell(9,1)->Range->{Text} = "Reset";

    $tmp = ($REG_ARR[$reg_cnt]{resetVal} >> 16) & 0xffff ;
    for ($col = 0 ; $col < 16 ; $col ++) {
        $rbit1[$col] = ($tmp >> $col) & 0x1;
    }
    $tmp = ($REG_ARR[$reg_cnt]{resetVal} ) & 0xffff ;
    for ($col = 0 ; $col < 16 ; $col ++) {
        $rbit0[$col] = ($tmp >> $col) & 0x1;
    }

    for ($col = 2 ; $col <= 17 ; $col ++) {
        $table->Cell(2,$col)->Range->{Text} = 31-$col+2 ;
        $table->Cell(6,$col)->Range->{Text} = 15-$col+2 ;

        $bit = $rbit1[17-$col] ;
        $table->Cell(5,$col)->Range->{Text} = $bit ;
        if ($bit == 1) { $table->Cell(5,$col)->Shading->{BackgroundPatternColor} = wdColorLightYellow ; }
        $bit = $rbit0[17-$col] ;
        $table->Cell(9,$col)->Range->{Text} = $bit ;
        if ($bit == 1) { $table->Cell(9,$col)->Shading->{BackgroundPatternColor} = wdColorLightYellow ; }
    }

    for ($row = 1 ; $row <= 9 ; $row ++) {
        if ($row == 1) { $range = $document->Range($table->Cell($row, 1)->Range->{Start}, $table->Cell($row,  3)->Range->{End}) ; }
        else           { $range = $document->Range($table->Cell($row, 1)->Range->{Start}, $table->Cell($row, 17)->Range->{End}) ; }
        $range->Cells->{VerticalAlignment}   = wdCellAlignVerticalTop ;
        $range->ParagraphFormat->{Alignment} = wdAlignParagraphCenter ;
        $range->Font->{Size} = 7;
    }
    $table->Rows(2)->Range->Font->{Size} = 8;
    $table->Rows(6)->Range->Font->{Size} = 8;
    $table->Rows(1)->Range->Font->{Size} = 8;
    $table->Rows(1)->Range->Font->{Bold} = wdToggle;

    $range = $document->Range($table->Cell(1, 1)->Range->{Start}, $table->Cell(9, 17)->Range->{End}) ;
    $range->ParagraphFormat->{SpaceBeforeAuto} = 0 ;
    $range->ParagraphFormat->{SpaceAfterAuto}  = 0 ;
    $range->ParagraphFormat->{LineSpacingRule} = wdLineSpaceExactly ;
    $range->ParagraphFormat->{LineSpacing}     = 10 ;

    $rcnt0 = 0 ;    $rcnt1 = 0 ;
    for ($fld_cnt = 0 ; $fld_cnt < $REG_ARR[$reg_cnt]{fld_cnt} ; $fld_cnt ++) {
        $fld_name = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{name} ;
        $fld_sp   = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{sp} ;
        $fld_ep   = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{ep} ;
        $fld_typ  = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{typ} ;
        $fld_typ  = &get_fld_typ($fld_typ) ;

       #print ("$fld_sp, $fld_ep\n") ;
        if ($fld_sp < 16 && $fld_ep >= 16) {
            $sp1 = 33 - $fld_ep ;     $ep1 = 17 ;
            $sp0 = 2 ;                $ep0 = 17 - $fld_sp ;
            $table->Cell(3,$sp1)->Range->{Text} = $fld_name ;
            $table->Cell(7,$sp0)->Range->{Text} = $fld_name ;

            $mpos0[$rcnt0] = $ep0 + ($sp0 << 5) ;    $rcnt0 ++ ;
            $mpos1[$rcnt1] = $ep1 + ($sp1 << 5) ;    $rcnt1 ++ ;

            &set_gray_for_reserved($table, 3, $sp1, $ep1, $fld_name) ;
            &set_gray_for_reserved($table, 7, $sp0, $ep0, $fld_name) ;
        } elsif ($fld_ep <  16) {
            $sp1 = 10 ;               $ep1 = 0 ;
            $sp0 = 17 - $fld_ep ;     $ep0 = 17 - $fld_sp ;
            $table->Cell(7,$sp0)->Range->{Text} = $fld_name ;
            $mpos0[$rcnt0] = $ep0 + ($sp0 << 5) ;    $rcnt0 ++ ;

            &set_gray_for_reserved($table, 7, $sp0, $ep0, $fld_name) ;
        } elsif ($fld_sp >= 16) {
            $sp1 = 33 - $fld_ep ;     $ep1 = 33 - $fld_sp ;
            $sp0 = 10 ;               $ep0 = 0 ;
            $table->Cell(3,$sp1)->Range->{Text} = $fld_name ;
            $mpos1[$rcnt1] = $ep1 + ($sp1 << 5) ;    $rcnt1 ++ ;

            &set_gray_for_reserved($table, 3, $sp1, $ep1, $fld_name) ;
        }

        for ($cnt = $sp1 ; $cnt <= $ep1 ; $cnt ++) {
            $table->Cell(4,$cnt)->Range->{Text} = $fld_typ ;
        }
        for ($cnt = $sp0 ; $cnt <= $ep0 ; $cnt ++) {
            $table->Cell(8,$cnt)->Range->{Text} = $fld_typ ;
        }
    }

    # merge
    for ($cnt = 0 ; $cnt <= $#mpos0 ; $cnt ++) {
        $sp0 = ($mpos0[$cnt] >> 5) & 0x1f ;
        $ep0 =  $mpos0[$cnt]       & 0x1f ;
        if ($ep0 - $sp0 > 0) {
           #print ("$cnt, $sp0, $ep0\n") ;
            $range = $document->Range($table->Cell(7, $sp0)->Range->{Start}, $table->Cell(7, $ep0)->Range->{End}) ; $range->Cells->Merge ;
        }
    }
    for ($cnt = 0 ; $cnt <= $#mpos1 ; $cnt ++) {
        $sp0 = ($mpos1[$cnt] >> 5) & 0x1f ;
        $ep0 =  $mpos1[$cnt]       & 0x1f ;
        if ($ep0 - $sp0 > 0) {
           #print ("$cnt, $sp0, $ep0\n") ;
            $range = $document->Range($table->Cell(3, $sp0)->Range->{Start}, $table->Cell(3, $ep0)->Range->{End}) ; $range->Cells->Merge ;
        }
    }

    $table->AutoFitBehavior(wdAutoFitWindow) ;
   #$table->AutoFitBehavior(wdAutoFitContent) ;
   #$table->AutoFitBehavior(wdAutoFitFixed) ;

   #$range = $document->Range($table->Cell(2,1)->Range->{Start}, $table->Cell(9,1)->Range->{End}) ;
   #$range->Columns->{PreferredWidthType} = wdPreferredWidthPoints ; #wdPreferredWidthPercent ;
   #$range->Columns->{PreferredWidth}     = 30 ;

    $tot_line = 50 ;
    return $tot_line ;
} #}}}

#----------------------------------------------------------------------------------------------------

sub gen_table_details ($$) { #{{{
    my ($table, $reg_cnt) = @_ ;
    my ($fld_sp, $fld_ep, $fld_name, $fld_typ, $fld_desc, $resetval, $tot_row) ;
    my ($cnt, $col, $row, $range, $tot_line, $cur_row) ;

    $tot_row = $REG_ARR[$reg_cnt]{fld_cnt} + 1 ;

    $tot_line = 0 ;

    $table->Borders(wdBorderLeft)      ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderLeft)      ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderRight)     ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderRight)     ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderTop)       ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderTop)       ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderBottom)    ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderBottom)    ->{LineWidth} = wdLineWidth150pt  ;
    $table->Borders(wdBorderHorizontal)->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderHorizontal)->{LineWidth} = wdLineWidth100pt  ;
    $table->Borders(wdBorderVertical)  ->{LineStyle} = wdLineStyleSingle ;
    $table->Borders(wdBorderVertical)  ->{LineWidth} = wdLineWidth100pt  ;

    #$table->Columns(1)->Borders(wdBorderRight)->{LineStyle} = wdLineStyleSingle ;
    #$table->Columns(1)->Borders(wdBorderRight)->{LineWidth} = wdLineWidth150pt  ;
    $table->Rows(1)->Borders(wdBorderBottom)->{LineStyle} = wdLineStyleSingle ;
    $table->Rows(1)->Borders(wdBorderBottom)->{LineWidth} = wdLineWidth150pt  ;

    $table->Cell(1,1)->Range->{Text} = "Field Name" ;
    $table->Cell(1,2)->Range->{Text} = "Bit" ;
    $table->Cell(1,3)->Range->{Text} = "Type" ;
    $table->Cell(1,4)->Range->{Text} = "Reset\nValue" ;
    $table->Cell(1,5)->Range->{Text} = "Description" ;
    $table->Rows(1)->Range->Font->{Bold} = wdToggle;

    for ($row = 1 ; $row <= $tot_row ; $row ++) {
        if ($row == 1) { 
            $range = $document->Range($table->Cell($row, 1)->Range->{Start}, $table->Cell($row,  5)->Range->{End}) ; 
            $range->Cells->{VerticalAlignment}   = wdCellAlignVerticalTop ;
            $range->ParagraphFormat->{Alignment} = wdAlignParagraphCenter ;
        } else { 
            $range = $document->Range($table->Cell($row, 1)->Range->{Start}, $table->Cell($row,  5)->Range->{End}) ; 
            $range->Cells->{VerticalAlignment}   = wdCellAlignVerticalTop ;
        }
    }

    $range = $document->Range($table->Cell(1, 1)->Range->{Start}, $table->Cell($tot_row, 5)->Range->{End}) ;
    $range->ParagraphFormat->{SpaceBeforeAuto} = 0 ;
    $range->ParagraphFormat->{SpaceAfterAuto}  = 0 ;
    $range->ParagraphFormat->{LineSpacingRule} = wdLineSpaceExactly ;
    $range->ParagraphFormat->{LineSpacing}     = 10 ;

    $resetval = $REG_ARR[$reg_cnt]{resetVal} ;
    for ($fld_cnt = 0 ; $fld_cnt < $REG_ARR[$reg_cnt]{fld_cnt} ; $fld_cnt ++) {
        $fld_name = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{name} ;
        $fld_sp   = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{sp} ;
        $fld_ep   = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{ep} ;
        $fld_typ  = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{typ} ;
        $fld_desc = $REG_ARR[$reg_cnt]{fld}[$fld_cnt]{desc} ;

        $fld_typ  = &get_fld_typ($fld_typ) ;

        $cur_row = $tot_row-$fld_cnt ;
            $table->Cell($cur_row,1)->Range->{Text} = sprintf("%s",          $fld_name       ) ;
        if ($fld_ep == $fld_sp) {
            $table->Cell($cur_row,2)->Range->{Text} = sprintf("[%2d]",       $fld_ep         ) ;
        } else {
            $table->Cell($cur_row,2)->Range->{Text} = sprintf("[%2d:%2d]",   $fld_ep, $fld_sp) ;
        }
            $table->Cell($cur_row,3)->Range->{Text} = sprintf("%s",          $fld_typ        ) ;
            $tmp = &get_reset_val($fld_sp, $fld_ep, $resetval) ;
            $table->Cell($cur_row,4)->Range->{Text} = sprintf("0x%X",        $tmp            ) ;
        if (defined($fld_desc)) {
            chomp($fld_desc) ;
            $table->Cell($cur_row,5)->Range->{Text} = sprintf("%s",          $fld_desc       ) ;
        }

        if (lc($fld_name) eq "reserved") {
            $table->Rows($cur_row)->Shading->{BackgroundPatternColor} = wdColorGray25 ;
        }

    }

   #$table->AutoFitBehavior(wdAutoFitWindow) ;
    $table->AutoFitBehavior(wdAutoFitContent) ;

    $tot_line = 200 ;
    return $tot_line ;
} #}}}

#----------------------------------------------------------------------------------------------------

sub set_gray_for_reserved ($$$$$) { #{{{
    my ($table, $row, $sp, $ep, $fld_name) = @_ ;
    if (lc($fld_name) eq "reserved") {
        $range = $document->Range($table->Cell($row,$sp)->Range->{Start}, $table->Cell($row+2,$ep)->Range->{End}) ; 
        $range->Shading->{BackgroundPatternColor} = wdColorGray25 ;
    }
} #}}}

#----------------------------------------------------------------------------------------------------

sub get_fld_typ ($) { #{{{
    my ($fld_typ) = @_ ;
    if ($fld_typ =~ /read-only/) {
        $fld_typ = "RO" ;
    } elsif ($fld_typ =~ /write-only/) {
        $fld_typ = "WO" ;
    } elsif ($fld_typ =~ /write-clear/) {
        $fld_typ = "WC" ;
    } elsif ($fld_typ =~ /read-write/) {
        $fld_typ = "RW" ;
    }

    return $fld_typ ;
} #}}}

#----------------------------------------------------------------------------------------------------

sub get_reset_val ($$$) { #{{{
    my ($sp, $ep, $rval) = @_ ;
    my $ret = 0 ;
    my $mask ;

    $ret = $rval >> $sp ;
    $mask = 2**($ep-$sp+1) - 1 ;
    $ret = $ret & $mask ;

    return $ret ;
} #}}}


#}}}


###############################################################################


use Cwd;

my $cur_dir = getcwd ;
my $xls_name ;

die "Usage: $0 xls_name work_dir\n" if $#ARGV != 1 ;

$xls_name = $ARGV[0] ;
$cur_dir  = $ARGV[1] ;

$rb_log_fn = "$cur_dir/regs_builder.log" ;
open RB_LOG_FH, ">$rb_log_fn" || die "Can't open file: $rb_log_fn" ;

&reg_parser_main     ("$xls_name",            $cur_dir) ;
&reg_manual_gen_main ("$cur_dir/reg_tbl.txt", $cur_dir) ;

close RB_LOG_FH ;

###############################################################################

# vim: fdm=marker
